﻿namespace Devonline.Core;

/// <summary>
/// 命令行参数处理工具类
/// TODO TBC
/// written by 林选臣
/// modified by devonline.cn 2020-2-24 17:38:18
/// 
/// usage: eg:  --dir /home/app/xxx
/// var arguments = CommandLineArgumentParser.Parse(args);
/// var arg = arguments.Get("--dir").Take();
/// arg => /home/app/xxx
/// 
/// 复杂命令行参数请使用 CommandLineParser 库
/// nuget => Install-Package CommandLineParser
/// </summary>
public class CommandLineArgumentParser
{
    private readonly List<CommandLineArgument> _arguments;

    public CommandLineArgumentParser(string[] args)
    {
        _arguments = [];
        if (args is not null)
        {
            for (var i = 0; i < args.Length; i++)
            {
                _arguments.Add(new CommandLineArgument(_arguments, i, args[i]));
            }
        }
    }

    public IEnumerable<CommandLineArgument> GetEnumerator()
    {
        foreach (var arg in _arguments)
        {
            yield return arg;
        }
    }

    public static CommandLineArgumentParser Parse(string[] args) => new(args);

    public CommandLineArgument? Get(string argumentName) => _arguments.FirstOrDefault(p => p == argumentName);

    public bool Has(string argumentName) => _arguments.Any(p => p == argumentName);

    /// <summary>
    /// 获取 参数 值 形式的参数的值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="argumentName"></param>
    /// <returns></returns>
    public T? GetValue<T>(string argumentName) where T : IConvertible
    {
        if (Has(argumentName))
        {
            var argument = Get(argumentName);
            if (argument is not null)
            {
                argument = argument.Take();
                if (argument is not null)
                {
                    return argument.To<T>();
                }
            }
        }

        return default;
    }
}

/// <summary>
/// 命令行参数
/// </summary>
public class CommandLineArgument
{
    private readonly List<CommandLineArgument> _arguments;
    private readonly string _argumentText;
    private readonly int _index;

    internal CommandLineArgument(List<CommandLineArgument> args, int index, string argument)
    {
        _arguments = args;
        _index = index;
        _argumentText = argument;
    }

    public CommandLineArgument? Next => (_index < _arguments.Count - 1) ? _arguments[_index + 1] : null;

    public CommandLineArgument? Previous => (_index > 0) ? _arguments[_index - 1] : null;

    public CommandLineArgument? Take() => Next;

    public IEnumerable<CommandLineArgument> Take(int count)
    {
        var list = new List<CommandLineArgument>();
        var parent = this;
        for (var i = 0; i < count; i++)
        {
            var next = parent.Next;
            if (next == null)
                break;

            list.Add(next);
            parent = next;
        }

        return list;
    }

    public static implicit operator string(CommandLineArgument argument) => argument._argumentText;

    public T? To<T>() where T : IConvertible => string.IsNullOrWhiteSpace(_argumentText) ? default : _argumentText.To<T>();

    public override string ToString() => _argumentText;
}